﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace NDF.Utilities
{
    /// <summary>
    /// 提供一组对 基础对象类型 <see cref="System.Object"/> 操作方法的扩展。
    /// </summary>
    public static partial class ObjectExtensions
    {
        /// <summary>
        /// 将当前对象转换成一个包含键值对集合的 <see cref="Dictionary"/> 对象。键值对中的 Key 值表示对象中的属性名，Value 值表示该属性名所对应的值。
        /// 当前对象中所有公共属性名及属性值都将包含在返回的键值对枚举列表中。
        /// </summary>
        /// <param name="_this">要被转换的对象。</param>
        /// <returns>返回一个 <see cref="Dictionary"/>。该对象键值对中的 Key 值表示对象中的属性名，Value 值表示该属性名所对应的值。</returns>
        public static Dictionary<string, object> ToDictionary(this object _this)
        {
            Check.NotNull(_this);
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            PropertyInfo[] properies = _this.GetType().GetProperties();
            foreach (PropertyInfo property in properies)
            {
                dictionary.Add(property.Name, property.GetValue(_this));
            }
            return dictionary;
        }

        /// <summary>
        /// 将当前对象转换成一个包含键值对集合的 <see cref="Dictionary"/> 对象。键值对中的 Key 值表示对象中的属性名，Value 值表示该属性名所对应的值。
        /// 当前对象中所有指定作用域范围的属性名及属性值都将包含在返回的键值对枚举列表中。
        /// </summary>
        /// <param name="_this">要被转换的对象。</param>
        /// <param name="bindingAttr">指定搜索 <paramref name="_this"/> 中属性的反射搜索范围。</param>
        /// <returns>返回一个 <see cref="Dictionary"/>。该对象键值对中的 Key 值表示对象中的属性名，Value 值表示该属性名所对应的值。</returns>
        public static Dictionary<string, object> ToDictionary(this object _this, BindingFlags bindingAttr)
        {
            Check.NotNull(_this);
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            PropertyInfo[] properies = _this.GetType().GetProperties(bindingAttr);
            foreach (PropertyInfo property in properies)
            {
                dictionary.Add(property.Name, property.GetValue(_this));
            }
            return dictionary;
        }


        /// <summary>
        /// 将当前对象中所有属性的值按照属性名称和类型定义的匹配关系复制到另一对象中。
        /// </summary>
        /// <param name="_this">
        /// 表示将要用于复制数据到另一对象的元数据对象。
        /// 如果该参数值为 Null，将不会执行复制操作。
        /// </param>
        /// <param name="element">表示一个目标对象，该对象中的相应属性值将会被更改。</param>
        /// <param name="throwError">一个布尔类型值，该值表示在复制数据过程中如果出现异常，是否立即停止并抛出异常，默认为 false。</param>
        public static void CopyTo(this object _this, object element, bool throwError = false)
        {
            if (_this == null)
            {
                return;
            }
            Check.NotNull(element);
            Type thisType = _this.GetType(), elemType = element.GetType();
            PropertyInfo[] thisProps = thisType.GetProperties(), elemProps = elemType.GetProperties();
            foreach (PropertyInfo thisProperty in thisProps)
            {
                PropertyInfo elemProperty = elemProps.FirstOrDefault(p => p.Name == thisProperty.Name);
                if (elemProperty != null && elemProperty.PropertyType.IsAssignableFrom(thisType))
                {
                    Utility.TryCatchExecute(() => elemProperty.SetValue(element, thisProperty.GetValue(_this)));
                }
            }
        }


        /// <summary>
        /// 将当前对象转换成一个指定类型的新对象。
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <paramref name="resultType"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <paramref name="resultType"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </summary>
        /// <param name="_this">要转换的对象。</param>
        /// <param name="resultType">要转换的目标实体类型。</param>
        /// <param name="throwError">一个布尔类型值，该值表示在复制数据过程中如果出现异常，是否立即停止并抛出异常，默认为 false。</param>
        /// <returns>
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <paramref name="resultType"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <paramref name="resultType"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </returns>
        public static object CastTo(this object _this, Type resultType, bool throwError = false)
        {
            Check.NotNull(_this);
            Check.NotNull(resultType);
            if (resultType.IsInstanceOfType(_this))
            {
                return _this;
            }
            object ret = Activator.CreateInstance(resultType);
            _this.CopyTo(ret, throwError);
            return ret;
        }

        /// <summary>
        /// 将当前对象转换成一个指定类型的新对象。
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <typeparamref name="TResult"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <typeparamref name="TResult"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </summary>
        /// <typeparam name="TResult">要转换的目标实体类型。</typeparam>
        /// <param name="_this">要转换的对象。</param>
        /// <param name="throwError">一个布尔类型值，该值表示在复制数据过程中如果出现异常，是否立即停止并抛出异常，默认为 false。</param>
        /// <returns>
        /// 该方法首先判断 <paramref name="_this"/> 是否为 <typeparamref name="TResult"/> 指定的类型，如果是，则直接返回 <paramref name="_this"/>；
        /// 否则将通过 Activator.CreateInstance(resultType) 方法创建一个 <typeparamref name="TResult"/> 类型的对象，然后查找出该类型对象有哪些公共属性值，将 <paramref name="_this"/> 对象的对应属性值复制到新创建的对象中。
        /// </returns>
        public static TResult CastTo<TResult>(this object _this, bool throwError = false)
        {
            if (_this is TResult)
            {
                return (TResult)_this;
            }
            Check.NotNull(_this);
            TResult ret = (TResult)Activator.CreateInstance(typeof(TResult));
            _this.CopyTo(ret, throwError);
            return ret;
        }
    }
}
